Bezier Curves

What if you need something smoother than a line of straight segments?

Bezier curves? We got 'em


(bez-curve
 [[0 1] [-1.5 -0.5] [-0.5 -1.5] [0 0]]
 {:stroke (p-color 255 128 64), :stroke-weight 4})

Perhaps we can use this to make smoothly curved corners for a frame

Note : framed is a Layout that takes three arguments :

  • a list of corner pieces (which it reflects appropriately),
  • a list of edge pieces (which it rotates appropriately)
  • and a single centre (NOT a list, just a single group which it fills the middle.)

(let
 [orange
  {:stroke (p-color 255 128 64), :stroke-weight 2}
  blue
  {:stroke (p-color 100 100 200), :stroke-weight 2}]
 (framed
  9
  (repeat
   (bez-curve
    [[0.9 -0.9] [-1.4 -0.9] [-0.9 -1.4] [-0.9 0.9]]
    orange))
  (repeat [{:style orange, :points [[-0.9 -1] [-0.9 -1] [-0.9 1]]}])
  (->> (drunk-line 10 0.1 blue) (clock-rotate 7))))

Framed can be used to generate various bordered patterns


(let
 [corner
  (stack
   (square {:fill (p-color 255 0 0 100)})
   (poly 0 0 0.9 4 {:fill (p-color 240 100 240)}))
  edge
  (stack
   (square {:fill (p-color 0 0 255)})
   (poly 0 0 0.5 8 {:fill (p-color 150 150 255)}))
  centre
  (poly 0 0 0.9 30 {:fill (p-color 150 255 140)})]
 (framed 7 (repeat corner) (repeat edge) centre))

Which can be tiled together with grid-layout


(let
 [corner
  (stack
   (square {:fill (p-color 255 0 0 100)})
   (poly 0 0 0.9 4 {:fill (p-color 240 100 240)}))
  edge
  (stack
   (square {:fill (p-color 0 0 255)})
   (poly 0 0 0.5 8 {:fill (p-color 150 150 255)}))
  centre
  (poly 0 0 0.9 30 {:fill (p-color 150 255 140)})]
 (grid-layout
  5 
  (repeat (framed 7 (repeat corner) (repeat edge) centre))))

Random transformations

Note how we fill grids from lazy, infinite lists of shapes made with repeat.

We can map other functions to these streams, for example to randomly rotate them.


(let
 [orange (p-color 254 129 64)]
 (stack
  (square {:fill (p-color 50 80 100)})
  (grid-layout
   10
   (random-turn-groups
    (repeat
     [(->SShape
       {:fill (p-color 230 180 90), :stroke orange}
       [[-1 -1] [1 -1] [-1 1]])])))))

We can increase the complexity of the pattern, mixing streams of various sub-patterns.

For more information on transforming streams of sub-patterns with functional programming tools, see Functional Power for Patterning


(let
 [orange (p-color 254 129 64)]
 (stack
  (square {:fill (p-color 50 80 100)})
  (checked-layout
   18
   (cycle
    [(clock-rotate
      8
      (drunk-line 10 0.1 {:stroke orange, :stroke-weight 1}))
     (clock-rotate
      5
      (drunk-line 10 0.1 {:stroke orange, :stroke-weight 2}))])
   (random-turn-groups
    (repeat
     [(->SShape
       {:fill (p-color 230 180 90), :stroke orange}
       [[-1 -1] [1 -1] [-1 1]])])))))

A word about colour.

Colours can be defined with the function p-color which takes 1, 3 or 4 arguments. A single argument will give you a shade of grey between black (0) and white (255). Three arguments will get mapped to red, green and blue components. Four arguments will get mapped to red, green, blue and alpha (transparency) components.

A transparent green :

(p-color 150 255 150 150)

We can over-ride or supplement the styling of an existing group using the over-style function. Here we're going to supplement an already bright green clock-rated drunk-line with a transparent green fill. Note that the drunk-line is not closed but can still be filled.

And to see the transparency we can stack it on top of a basic grid of squares. (This may not work in all browsers.)


(stack
 (square {:fill (p-color 0)})
 (grid-layout
  10
  (repeat
   (square {:stroke-weight 2, :stroke (p-color 100 50 100)})))
 (over-style
  {:fill (p-color 150 255 150 150)}
  (clock-rotate
   12
   (drunk-line
    20 
    0.05 
    {:stroke (p-color 100 255 100), :stroke-weight 3}))))